interface IDefaultOptions {
  START_HEART_TIME?: number;
  END_HEART_TIME?: number;
  RECONNECT_MAX_NUM?: number;
  RECONNECT_TIME?: number;
}

const defaultOptions = {
  START_HEART_TIME: 6000,
  END_HEART_TIME: 5000,
  RECONNECT_MAX_NUM: 5,
  RECONNECT_TIME: 3000,
};

class Socket {
  url: string;
  ws!: WebSocket;
  // 心跳检测间隔时间
  START_HEART_TIME = 60000;
  END_HEART_TIME = 5000;
  // 心跳检测是否通过
  isHeartingSuccess = true;

  // 重连次数
  reconnectNum = 0;
  // 最大重连次数
  RECONNECT_MAX_NUM = 5;
  // 重连时间
  RECONNECT_TIME = 30000;

  // 存储函数的容器
  callbacks: Function[] = [];

  constructor(url: string, options?: IDefaultOptions) {
    this.url = url;
    const mergeOptions = Object.assign(defaultOptions, options);
    this.START_HEART_TIME = mergeOptions.START_HEART_TIME!;
    this.RECONNECT_MAX_NUM = mergeOptions.RECONNECT_MAX_NUM!;
    this.END_HEART_TIME = mergeOptions.END_HEART_TIME!;
    this.RECONNECT_TIME = mergeOptions.RECONNECT_TIME!;
    this.init();
  }

  init() {
    // 链接上web socket
    const ws = new WebSocket(this.url);

    // 监听ws是否连接成功
    ws.onopen = this.handleOpen;

    // 监听ws是否连接断开
    ws.onclose = this.handleClose;

    this.ws = ws;
  }

  handleOpen = () => {
    // 重连成功需要清0重连次数
    this.reconnectNum = 0;
    // 开始心跳检测
    this.startHeart();
    // 接受服务器的消息
    this.ws.addEventListener("message", (e) => {
      let data = e.data;
      if (data === "pong") return;
      data = JSON.parse(data);
      this.callbacks.forEach((cb) => cb(data));
    });
  };

  handleClose = () => {
    // 断线重连
    // console.log("close");
    this.reconnect();
  };

  // 1. 心跳检测
  startHeart() {
    /*
      原理：每隔1分钟向服务器发送ping信号，服务器需要返回pong信号，
        前端需要检测是否接受到pong信号，
          如果接收到，说明连接正常，开始下一次心跳检测
          如果没有接收到，说明连接失败，开始断线重连
    */

    // 事件只能绑一次
    this.ws.addEventListener("message", (e) => {
      if (!this.isHeartingSuccess) {
        if (e.data === "pong") {
          this.isHeartingSuccess = true;
        }
      }
    });

    this.sendHeartMessage();
  }

  sendHeartMessage() {
    console.log("心跳检测开始");
    setTimeout(() => {
      this.isHeartingSuccess = false;
      this.ws.send("ping");
      setTimeout(() => {
        if (!this.isHeartingSuccess) {
          // console.log("心跳检测失败");
          // this.ws.close();
          this.reconnect(); // 断线重连
          this.isHeartingSuccess = true;
        } else {
          // console.log("心跳检测成功，开始新一轮的心跳检测");
          this.sendHeartMessage();
        }
      }, this.END_HEART_TIME);
    }, this.START_HEART_TIME);
  }

  // 2. 断线重连
  // 1. 每次断线重连间隔不能太短 - 30s
  // 2. 断线重连有最大次数 - 5
  reconnect() {
    console.log("断线重连开始");

    if (this.reconnectNum >= this.RECONNECT_MAX_NUM) {
      // 提示
      alert("WebSocket连接失败~");
      return;
    }
    setTimeout(() => {
      this.reconnectNum++;
      this.init();
    }, this.RECONNECT_TIME);
  }

  // 3. 封装接受数据的方法
  onmessage(cb: Function) {
    this.callbacks.push(cb);
  }
}

export const useSocket = (url: string) => {
  return new Socket(url);
};

export default Socket;
